package model

import (
	"common/helper"
	myUserHelp "common/userHelp"
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	g "github.com/doug-martin/goqu/v9"
	"github.com/go-redis/redis/v8"
	"github.com/meilisearch/meilisearch-go"
	"github.com/valyala/fasthttp"
	"github.com/valyala/fastjson"
)

func FavInsert(fctx *fasthttp.RequestCtx, id, ty string) error {

	a := &fastjson.Arena{}
	game := Game{}
	ex := g.Ex{
		"id": id,
	}

	ty_allow := map[string]bool{
		"":    true,
		"rec": true,
		"hot": true,
		"fav": true,
	}
	if _, ok := ty_allow[ty]; !ok {
		return errors.New(helper.ParamErr)
	}

	query, _, _ := dialect.From("tbl_game_lists").Select(colsGame...).Where(ex).Limit(1).ToSQL()
	err := meta.MerchantDB.Get(&game, query)
	if err != nil && err != sql.ErrNoRows {
		return pushLog(fmt.Errorf("%s,[%s]", err.Error(), query), helper.DBErr)
	}

	if err == sql.ErrNoRows {
		return errors.New(helper.RecordNotExistErr)
	}

	uid := GetUidFromToken(fctx)
	if uid == "" {
		return errors.New(helper.AccessTokenExpires)
	}

	myUserHelp.LoadUserToRedis(uid)

	sid := helper.GenId()
	ts := int(fctx.Time().Unix())
	code := fmt.Sprintf("%s:%s", id, uid)

	obj := a.NewObject()

	obj.Set("id", a.NewStringBytes([]byte(sid)))
	obj.Set("ty", a.NewStringBytes([]byte(ty)))
	obj.Set("code", a.NewStringBytes([]byte(code)))
	obj.Set("uid", a.NewStringBytes([]byte(uid)))
	obj.Set("game_id", a.NewStringBytes([]byte(id)))
	obj.Set("platform_id", a.NewStringBytes([]byte(game.PlatformID)))
	obj.Set("game_type", a.NewNumberInt(game.GameType))
	obj.Set("created_at", a.NewNumberInt(ts))
	obj.Set("hot", a.NewNumberInt(game.IsHot))

	aa := a.NewArray()
	aa.SetArrayItem(0, obj)

	str := aa.MarshalTo(nil)

	index := meta.Meili.Index("favs")
	_, err = index.AddDocuments(str, "id")

	if err == nil {
		key := fmt.Sprintf("fav:%s", uid)

		pipe := meta.MerchantRedis.Pipeline()
		defer pipe.Close()

		pipe.SAdd(ctx, key, id)
		pipe.Persist(ctx, key)
		pipe.Exec(ctx)
	}

	return err
}

func FavList(fctx *fasthttp.RequestCtx, gameType, isHot int, platform_id, ty string) ([]TblGameLists, error) {

	var data []TblGameLists

	ids := []string{}

	uid := GetUidFromToken(fctx)
	if uid == "" {
		return data, errors.New(helper.AccessTokenExpires)
	}

	myUserHelp.LoadUserToRedis(uid)

	ty_allow := map[string]bool{
		"rec": true,
		"hot": true,
		"fav": true,
	}

	cond := &meilisearch.SearchRequest{
		Limit:  100,
		Offset: 0,
	}

	index := meta.Meili.Index("favs")
	filter := fmt.Sprintf("uid = %s", uid)
	if isHot > 0 {
		filter += fmt.Sprintf(" AND hot = %d", isHot)
	}
	if platform_id != "0" && helper.CtypeDigit(platform_id) {
		filter += fmt.Sprintf(" AND platform_id = %s", platform_id)
	}
	if gameType > 0 {
		filter += fmt.Sprintf(" AND game_type = %d", gameType)
	}
	if _, ok := ty_allow[ty]; ok {
		filter += fmt.Sprintf(" AND ty = %s", ty)
	}

	cond.Filter = filter
	searchRes, err := index.Search("", cond)
	if err != nil {
		return data, err
	}

	ll := len(searchRes.Hits)
	if ll == 0 {
		return data, err
	}

	for _, v := range searchRes.Hits {

		val := v.(map[string]interface{})
		ids = append(ids, val["game_id"].(string))
	}

	pipe := meta.MerchantRedis.Pipeline()
	defer pipe.Close()

	data = make([]TblGameLists, ll)
	results := make([]*redis.StringCmd, ll)
	exists := make([]*redis.IntCmd, ll)

	for i := 0; i < ll; i++ {
		exists[i] = pipe.Exists(ctx, "g:"+ids[i])
		results[i] = pipe.Get(ctx, "g:"+ids[i])
	}
	pipe.Exec(ctx)

	for i := 0; i < ll; i++ {
		if exists[i].Val() == 0 {
			continue
		}
		b, err := results[i].Bytes()
		if err != nil {
			return data, pushLog(fmt.Errorf("%s,[%s]", err.Error(), "FavList results[i].Bytes"), helper.DBErr)
		}

		err = json.Unmarshal(b, &data[i])
		if err != nil {
			return data, pushLog(fmt.Errorf("%s,[%s]", err.Error(), "FavList json.Unmarshal"), helper.DBErr)
		}

		data[i].IsFav = 1
	}
	return data, nil
}

func FavDelete(fctx *fasthttp.RequestCtx, id string) error {
	uid := GetUidFromToken(fctx)
	if uid == "" {
		return errors.New(helper.AccessTokenExpires)
	}

	myUserHelp.LoadUserToRedis(uid)

	filter := fmt.Sprintf("uid = %s AND game_id = %s", uid, id)
	index := meta.Meili.Index("favs")
	_, err := index.DeleteDocumentsByFilter(filter)

	if err == nil {
		key := fmt.Sprintf("fav:%s", uid)
		meta.MerchantRedis.SRem(ctx, key, id)
	}
	return err
}

func FavFlushAll() {

	filterable := []string{
		"ty",
		"uid",
		"hot",
		"game_type",
		"game_id",
		"platform_id",
	}
	sortable := []string{
		"created_at",
	}
	searchable := []string{
		"ty",
	}

	meta.Meili.DeleteIndex("favs")
	index := meta.Meili.Index("favs")
	index.UpdateFilterableAttributes(&filterable)
	index.UpdateSortableAttributes(&sortable)
	index.UpdateSearchableAttributes(&searchable)
	index.UpdateDistinctAttribute("code")
}
